MVP矩阵推导
其中Model, View的矩阵部分可根据Local转World坐标系转换得到,可参考龙书5.6.1章节。
透视投影矩阵可以分为x,y的投影变换,以及z的透视校正
首先x,y部分可以根据透视变换的相似三角形推导出,此处x部分的FOV令为θ,横纵坐标比aspect令为r=hw。
对应x,y的转换细节推导可参考透视投影详解
其中(3)中的x′′,y′′已经归一化转化到了NDC空间(注意要用2θ)
x′′=zxcot(2θ)r1y′′=zycot(2θ)
此时z没有做过变换,本质上若需要归一化,也只需要做一个从区间[n,f]到[0,1]的一个变换即可。但若z仅仅只是做了一个区间映射而没有经过透视校正,则会出现下述问题。
Vertex Shader完成顶点变换后需要做光栅化步骤,此时会在投影平面上进行均匀采样。根据上图中,对屏幕上线段进行均匀采样,而其对应线段中的点并非均匀排布。观察发现对应线段上点的x,y部分仍然保持均匀分布,但z由于透视变换的问题造成结果的畸变。
后续的纹理,颜色插值计算也会因为z轴的透视问题带来非均匀分布的问题
本质上的问题是因为z在透视变换后,是非线性分布的。因此对应的解决方案就是找到一个能够满足深度前后关系不变,且能支持线性插值的量来代替原有的z。
此处证明z1是线性分布的。令直线为ax+bz=c,给出线段上一点[x,z],其对应投影平面上的点为[p,−e],即有
xp=z−e(−eap+b)z=cz1=−ceap+cb
设线段的头尾点为[x1,z1],[x2,z2]以及对应投影平面上的点为[p1,−e],[p2,−e]。令p3=(1−t)p1+tp2由p1,p2线性插值得出,求[p3,−e]对应的线段上的点。
z31====−ceap3+cb−ceap1(1−t)−−ceap2t+cb(−ceap1+cb)(1−t)+(−ceap2+cb)tz11(1−t)+z21t
可见z1在直线上可以线性插值。由于z轴在光栅化中需要被插值,因此直接构造z1使得硬件在做插值时保持线性,为了将z′归一化到[0,1]需要构造z′=A+zB+B。已知
01==A+nBA+fB
求解方程组,则有A=f−nf,B=f−nnf
可得最终透视投影矩阵
这里推导的结果为左手系。若左右手系区分则只需要调换部分元素位置,可见龙书推导。
⎣⎢⎢⎢⎡rtan(2θ)10000tan(2θ)10000f−nff−nnf0010⎦⎥⎥⎥⎤
Sophia Renderer上的矩阵
由于DirectX 11的GPU部分采用了列主的矩阵排布。一般有两种解决方案
- CPU端采用行主,GPU端采用列主,在CPU绑定矩阵到CB的时候转置一次。
- CPU端采用行主不变,GPU端直接左乘向量(GPU上的矩阵是CPU的转置)
采用两种方式不同的地方在于,前者MVP=Projection∗View∗World∗Vector,后者本质上是MVP=Vector∗WorldT∗ViewT∗ProjectionT,而由于列主是默认的,因此在GPU上看起来就像MVP=V∗W∗V∗P一样。本质上只是CPU端的转置结果。
这里Sophia Renderer采用第二种方式,即默认GPU端都为CPU端绑定结果的转置,因此选用左乘来得到正确结果。
Reference
- Mathematics for 3D Game Programming and Computer Graphics, Third Edition, Section 5.4
- Introduction to 3D GAME PROGRAMMING WITH DIRECTX® 11, Section 5.6